﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using RSSFeedReader.Data.Models;
using RSSFeedReader.Resources;
using RSSFeedReader.ViewModels.Messages;

namespace RSSFeedReader.ViewModels
{
    /// <summary>
    /// Base class for ChannelViewModel functionalities.
    /// </summary>
    public abstract class ManageChannelBaseViewModel : MyViewModelBase
    {
        public static string ChannelDragDropCompleted = "ChannelDragDropCompleted";

        #region Fields
        ObservableCollection<ChannelViewModel> _channels;

        ChannelViewModel _rootChannelViewModel;
        ChannelViewModel _currentChannel;
        #endregion

        #region Constructor
        /// <summary>
        /// Initialize a new instance of the ManageChannelBaseViewModel class.
        /// </summary>
        protected ManageChannelBaseViewModel()
        {
            Initialize();
        }

        /// <summary>
        /// Initialize data.
        /// </summary>
        void Initialize()
        {
            _channels = new ObservableCollection<ChannelViewModel>();
            ChannelDataSource.Instance.ChannelFolderAdded += OnChannelFolderAdded;
            ChannelDataSource.Instance.ChannelAdded += OnChannelAdded;
            ChannelDataSource.Instance.ChannelMovedToFolder += OnMoveToFolder;
            //Messenger.Default.Register<MyTreeViewDragEventArgs>( this, ChannelDragDropCompleted, OnChannelDragDropCompleted );
            ChannelDataSource.Instance.ChannelMoved += OnChannelMoved;

            _rootChannelViewModel = ChannelViewModel.CreateNew(
                new Channel
                {
                    Title = "Feeds",
                    ChannelType = ChannelType.Folder
                });
            _rootChannelViewModel.PropertyChanged += OnChannelViewModelPropertyChanged;
            _channels.Add(_rootChannelViewModel);

            foreach (var channelViewModel in ChannelDataSource.Instance.Channels.Select(ChannelViewModel.CreateNew))
            {
                InitializeChannelViewModel(channelViewModel);
                _rootChannelViewModel.Children.Add(channelViewModel);
                channelViewModel.Parent = _rootChannelViewModel;
            }

            _rootChannelViewModel.IsExpanded = true;
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets the collection of type <see cref="RSSFeedReader.ViewModels.ChannelViewModel"/>.
        /// </summary>
        public ObservableCollection<ChannelViewModel> Channels
        {
            get { return _channels; }
        }

        /// <summary>
        /// Gets the currently selected <see cref="RSSFeedReader.ViewModels.ChannelViewModel"/> instance.
        /// </summary>
        public ChannelViewModel CurrentChannel
        {
            get { return _currentChannel; }
            set
            {
                if (_currentChannel == value)
                    return;

                _currentChannel = value;
                RaisePropertyChanged("CurrentChannel");
            }
        }
        #endregion

        #region Commands

        #region AddNewFeedCommand
        private RelayCommand _addNewFeedCommand;

        /// <summary>
        /// Gets the <see cref="AddNewFeedCommand"/>.
        /// </summary>
        public RelayCommand AddNewFeedCommand
        {
            get
            {
                return _addNewFeedCommand ?? (_addNewFeedCommand = new RelayCommand(
                    () => Messenger.Default.Send(new ShowWindowMessage<AddNewFeedViewModel>(new AddNewFeedViewModel())),
                    () => ChannelDataSource.Instance.IsNetworkAvailable));
            }
        }
        #endregion

        #region NewFolderCommand
        private RelayCommand _newFolderCommand;

        /// <summary>
        /// Gets the <see cref="NewFolderCommand"/>.
        /// </summary>
        public RelayCommand NewFolderCommand
        {
            get
            {
                return _newFolderCommand ?? (_newFolderCommand = new RelayCommand(
                    () =>
                    {
                        if (_currentChannel == null || _currentChannel == _rootChannelViewModel)
                        {
                            Messenger.Default.Send(new ShowWindowMessage<AddNewFeedViewModel>(new AddNewFeedViewModel()));
                        }
                        else
                        {
                            Messenger.Default.Send(new ShowWindowMessage<AddNewFeedViewModel>
                                (new AddNewFeedViewModel(ChannelDataSource.Instance.FindChannelById(_currentChannel.ChannelID))));
                        }
                    }));
            }
        }
        #endregion

        #region DeleteChannelCommand
        private RelayCommand _deleteChannelCommand;

        /// <summary>
        /// Gets the <see cref="DeleteChannelCommand"/>.
        /// </summary>
        public RelayCommand DeleteChannelCommand
        {
            get
            {
                return _deleteChannelCommand ?? (_deleteChannelCommand = new RelayCommand(
                    () =>
                    {
                        string message = _currentChannel.ChannelType == ChannelType.Feed ? Strings.DeleteFeedConfirmation : Strings.DeleteFolderConfirmation;
                        ShowDialogMessage(
                            message,
                            Strings.ApplicationName,
                            MessageBoxButton.YesNo,
                            MessageBoxImage.Warning,
                            dialogResult =>
                            {
                                if (dialogResult == MessageBoxResult.Yes)
                                {
                                    ChannelDataSource.Instance.RemoveChannel(_currentChannel.ChannelID);
                                    _currentChannel.Parent.Children.Remove(_currentChannel);
                                }
                            });
                    }, () => _currentChannel != null && _currentChannel != _rootChannelViewModel));
            }
        }
        #endregion

        #region MoveToFolderCommand
        private RelayCommand _moveToFolderCommand;

        /// <summary>
        /// Gets the <see cref="MoveToFolderCommand"/>.
        /// </summary>
        public RelayCommand MoveToFolderCommand
        {
            get
            {
                return _moveToFolderCommand ?? (_moveToFolderCommand = new RelayCommand(
                    () => Messenger.Default.Send(new ShowWindowMessage<MoveToFolderViewModel>(new MoveToFolderViewModel(_currentChannel))),
                    () => _currentChannel != _rootChannelViewModel && _currentChannel != null));
            }
        }
        #endregion

        #region OkCommand
        private RelayCommand _okCommand;

        /// <summary>
        /// Gets the <see cref="OkCommand"/>.
        /// </summary>
        public virtual RelayCommand OkCommand { get { return _okCommand ?? (_okCommand = new RelayCommand(CloseView)); } }
        #endregion

        #endregion

        #region Protected Methods
        /// <summary>
        /// Returns an ObservableCollection of type ChannelViewModel that are ChannelType.Folder only.
        /// </summary>
        /// <returns>Returns an ObservableCollection of type ChannelViewModel that are ChannelType.Folder only.</returns>
        protected ObservableCollection<ChannelViewModel> GetChannelFolders()
        {
            foreach (var channel in ChannelDataSource.Instance.Channels)
            {
                if (channel.ChannelType == ChannelType.Feed)
                {
                    _rootChannelViewModel.Children.Remove(
                        Common.FindChannelViewModelById(channel.ChannelID, Channels.ToList()));
                }
                else
                {
                    GetChannelFoldersChildren(
                        Common.FindChannelViewModelById(channel.ChannelID, Channels.ToList()));
                }
            }

            return _channels;
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Initialize the specified <see cref="RSSFeedReader.ViewModels.ChannelViewModel"/>.
        /// </summary>
        /// <param name="channelViewModel">The <see cref="RSSFeedReader.ViewModels.ChannelViewModel"/> to initialize.</param>
        void InitializeChannelViewModel(ChannelViewModel channelViewModel)
        {
            channelViewModel.PropertyChanged += OnChannelViewModelPropertyChanged;
            foreach (ChannelViewModel channelVM in channelViewModel.Children)
            {
                channelVM.PropertyChanged += OnChannelViewModelPropertyChanged;
                InitializeChannelViewModel(channelVM);
            }
        }

        /// <summary>
        /// Filters for folders in the children of the ChannelViewModel specified.
        /// </summary>
        /// <param name="channelViewModel">The ChannelViewModel to search the children of.</param>
        private void GetChannelFoldersChildren(ChannelViewModel channelViewModel)
        {
            List<ChannelViewModel> channelsToRemove = new List<ChannelViewModel>();

            foreach (ChannelViewModel child in channelViewModel.Children)
            {
                if (child.ChannelType == ChannelType.Feed)
                {
                    // Store ChannelViewModels to remove in a temp List as we can't
                    // modify collection whilst iterating through it.
                    channelsToRemove.Add(child);
                }
                else
                {
                    GetChannelFoldersChildren(child);
                }
            }

            // Remove ChannelViewModels that were identified.
            foreach (ChannelViewModel channelVMToRemove in channelsToRemove)
            {
                channelViewModel.Children.Remove(channelVMToRemove);
            }
        }
        #endregion

        #region Event Handlers
        /// <summary>
        /// Event handler for when a channel folder is added.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Event arguments describing the event.
        /// </param>
        void OnChannelFolderAdded(object sender, ChannelFolderAddedEventArgs e)
        {
            if (e.Parent == null)
            {
                var newChannelViewModel = ChannelViewModel.CreateNew(e.NewFolder);
                InitializeChannelViewModel(newChannelViewModel);
                _rootChannelViewModel.Children.Add(newChannelViewModel);
            }
            else
            {
                var newChannelViewModelParent = Common.FindChannelViewModelById(e.Parent.ChannelID, Channels.ToList());
                var newChannelViewModel = ChannelViewModel.CreateNew(e.NewFolder);
                newChannelViewModel.Parent = newChannelViewModelParent;
                InitializeChannelViewModel(newChannelViewModel);
                newChannelViewModelParent.Children.Add(newChannelViewModel);
            }
        }

        /// <summary>
        /// Event handler for when a property of the <see cref="RSSFeedReader.ViewModels.ChannelViewModel"/>
        /// instance changes.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Event arguments describing the event.
        /// </param>
        void OnChannelViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            const string isSelected = "IsSelected";
            var channelVM = sender as ChannelViewModel;

            if (e.PropertyName == isSelected)
            {
                _currentChannel = channelVM;
            }
        }

        /// <summary>
        /// Event handler for when a channel feed is added.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Event arguments describing the event.
        /// </param>
        void OnChannelAdded(object sender, ChannelAddedEventArgs e)
        {
            if (e.Error != null)
                return;

            // Create the new ChannelViewModel and add to the _channelFolders collection.
            var channelViewModel = ChannelViewModel.CreateNew(e.NewChannel);
            InitializeChannelViewModel(channelViewModel);

            // Add to the _channelFolders collection.
            _rootChannelViewModel.Children.Add(channelViewModel);
        }

        /// <summary>
        /// Event handler for when a channel or folder is
        /// moved to another folder.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// Event arguments describing the event.
        /// </param>
        void OnMoveToFolder(object sender, ChannelMovedToFolderEventArgs e)
        {
            ChannelViewModel folderViewModel = null;
            ChannelViewModel channelViewModelToMove = Common.FindChannelViewModelById(e.ChannelToMove.ChannelID, _channels.ToList());

            if (channelViewModelToMove == null)
                return;

            if (e.DestinationFolder != null) // Could be null if moving to root
                folderViewModel = Common.FindChannelViewModelById(e.DestinationFolder.ChannelID, _channels.ToList());

            if (channelViewModelToMove.Parent != null && folderViewModel != null)
            {
                // Moving from folder to folder
                ChannelViewModel parent = Common.FindChannelViewModelById(channelViewModelToMove.Parent.ChannelID, _channels.ToList());
                parent.Children.Remove(channelViewModelToMove);
                folderViewModel.Children.Add(channelViewModelToMove);
                channelViewModelToMove.Parent = folderViewModel;
                folderViewModel.IsExpanded = true;
            }
            else if (folderViewModel != null)
            {
                // Coming from root to a folder
                Channels.Remove(channelViewModelToMove);
                folderViewModel.Children.Add(channelViewModelToMove);
                channelViewModelToMove.Parent = folderViewModel;
                folderViewModel.IsExpanded = true;
            }
            else
            {
                // Must be moving back to the root
                if (channelViewModelToMove.Parent != null)
                {
                    var parent = Common.FindChannelViewModelById(channelViewModelToMove.Parent.ChannelID, _channels.ToList());
                    parent.Children.Remove(channelViewModelToMove);
                }
                channelViewModelToMove.Parent = null;
                Channels.Add(channelViewModelToMove);
            }
        }

        /// <summary>
        /// Event handler for when a channel is moved.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">Event arguments describing the event.</param>
        void OnChannelMoved(object sender, ChannelMovedEventArgs e)
        {
            ChannelViewModel channelToMove;
            ChannelViewModel channelToMoveParent;
            ChannelViewModel channelToMoveTo;
            ChannelViewModel channelToMoveToParent;
            int channelToMoveToIndex;

            channelToMove = Common.FindChannelViewModelById(e.ChannelToMove.ChannelID, _channels.ToList());
            channelToMoveParent =
                channelToMove.Parent == null ? null : Common.FindChannelViewModelById(channelToMove.Parent.ChannelID, _channels.ToList());

            if (e.ChannelToMoveTo == null)
            {
                // Must be moving back to the root
                if (channelToMoveParent != null)
                    channelToMoveParent.Children.Remove(channelToMove);
                channelToMove.Parent = null;
                _channels.Add(channelToMove);
                return;
            }

            channelToMoveTo = Common.FindChannelViewModelById(e.ChannelToMoveTo.ChannelID, _channels.ToList());
            channelToMoveToParent = channelToMoveTo.Parent == null ?
                null : Common.FindChannelViewModelById(channelToMoveTo.Parent.ChannelID, _channels.ToList());

            if (channelToMoveParent != null && channelToMoveToParent != null)
            {
                channelToMoveToIndex = channelToMoveToParent.Children.IndexOf(channelToMoveTo);
                if (channelToMoveParent == channelToMoveToParent)
                {
                    // Moving from within the same folder
                    if (channelToMoveTo == channelToMoveToParent.Children.Last())
                    {
                        channelToMoveParent.Children.Remove(channelToMove);
                        channelToMoveToParent.Children.Add(channelToMove);
                    }
                    else if (channelToMoveTo == channelToMoveToParent.Children.First())
                    {
                        channelToMoveParent.Children.Remove(channelToMove);
                        channelToMoveToParent.Children.Insert(0, channelToMove);
                    }
                    else
                    {
                        channelToMoveParent.Children.Remove(channelToMove);
                        channelToMoveToParent.Children.Insert(channelToMoveToIndex, channelToMove);
                    }
                    channelToMove.Parent = channelToMoveToParent;
                }
                else
                {
                    // Moving from another folder
                    channelToMoveParent.Children.Remove(channelToMove);

                    if (channelToMoveTo == channelToMoveToParent.Children.Last())
                    {
                        channelToMoveToParent.Children.Add(channelToMove);
                    }
                    else if (channelToMoveTo == channelToMoveToParent.Children.First())
                    {
                        channelToMoveToParent.Children.Insert(0, channelToMove);
                    }
                    else
                    {
                        channelToMoveToParent.Children.Insert(channelToMoveToIndex, channelToMove);
                    }
                    channelToMove.Parent = channelToMoveToParent;
                }
            }
            else if (channelToMoveParent == null && channelToMoveToParent != null)
            {
                // Coming from root to a folder
                channelToMoveToIndex = channelToMoveToParent.Children.IndexOf(channelToMoveTo);
                _channels.Remove(channelToMove);

                if (channelToMoveTo == channelToMoveToParent.Children.Last())
                {
                    channelToMoveToParent.Children.Add(channelToMove);
                }
                else if (channelToMoveTo == channelToMoveToParent.Children.First())
                {
                    channelToMoveToParent.Children.Insert(0, channelToMove);
                }
                else
                {
                    channelToMoveToParent.Children.Insert(channelToMoveToIndex + 1, channelToMove);
                }

                channelToMove.Parent = channelToMoveToParent;
            }
            else if (channelToMoveParent != null)
            {
                // Coming from folder to root at a specified feed
                channelToMoveToIndex = _channels.IndexOf(channelToMoveTo);
                channelToMoveParent.Children.Remove(channelToMove);

                if (channelToMoveTo == _channels.Last())
                {
                    _channels.Add(channelToMove);
                }
                else if (channelToMoveTo == _channels.First())
                {
                    _channels.Insert(0, channelToMove);
                }
                else
                {
                    _channels.Insert(channelToMoveToIndex + 1, channelToMove);
                }

                channelToMove.Parent = null;
            }
            else
            {
                channelToMoveToIndex = _channels.IndexOf(channelToMoveTo);

                // Moving from within the same folder
                if (channelToMoveTo == _channels.Last())
                {
                    _channels.Remove(channelToMove);
                    _channels.Add(channelToMove);
                }
                else if (channelToMoveTo == _channels.First())
                {
                    _channels.Remove(channelToMove);
                    _channels.Insert(0, channelToMove);
                }
                else
                {
                    _channels.Remove(channelToMove);
                    _channels.Insert(channelToMoveToIndex, channelToMove);
                }
                channelToMove.Parent = null;
            }
        }
        #endregion
    }
}
